// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef NL_SHADOW_MAP_H
#define NL_SHADOW_MAP_H

#include "nel/misc/types_nl.h"
#include "nel/3d/texture.h"
#include "nel/3d/vertex_buffer.h"
#include "nel/misc/aabbox.h"


namespace NL3D
{


using	NLMISC::CPlane;
using	NLMISC::CRefPtr;
using	NLMISC::CAABBox;

class	CShadowMapManager;
class	CMaterial;


// ***************************************************************************
/**
 * This class encapsulate all Data generated by a Shadow Caster, and read for a Shadow Receiver.
 * \author Lionel Berenguier
 * \author Nevrax France
 * \date 2003
 */
class CShadowMap
{
public:
	/** Computed at shadow casting time. The matrix used to cast the Shadow.
	 *	It is actualy a World ProjectionMatrix, but is only local to the Position of the Model (not rotation/scale).
	 *	Hence receiver have to add the position of the caster model to this matrix to get true World.
	 *	The usage of this matrix is for UV projection: XYZ= WorldProjectionMatrix * UVW.
	 *	NB: Vj (ie for W) is mapped such that Vp means NearClip of the shadow and Vp+Vj means FarClip of the shadow
	 */
	NLMISC::CMatrix			LocalProjectionMatrix;

	/** Computed at shadow casting time. They are clipping planes used to clip receivers (mirror of the OBB).
	 *	Receivers may use them to clip sub received parts (as they which)
	 *	Like the ProjectionLocalMatrix, this plane are in World, but the position of the Caster model.
	 *	\see generateClipInfoFromMatrix()
	 */
	std::vector<CPlane>		LocalClipPlanes;

	/** Computed at shadow casting time. This is the LocalPos Bouding Box containing the shadow (AxisAligned).
	 *	\see generateClipInfoFromMatrix()
	 */
	NLMISC::CAABBox			LocalBoundingBox;


	// Filled by ShadowMapManager. This is the Last Frame Id we had update the texture.
	uint64					LastGenerationFrame;


	/** They are the fade of the shadowMap, for Lod Shadow display. 0-1 values.
	 *	NB: if DistanceFade==1 or TemporalOutScreenFade==1, then the ShadowMap Texture is released.
	 *	Final Fade is the max of the 3.
	 */
	// Value computed according to pos of skeleton and Max Shadow Display value.
	float					DistanceFade;
	// Value computed according to pos of skeleton and other skeletons in the area (whatever the visible state)
	float					TemporalOutScreenFade;
	// Value computed according to pos of skeleton and other skeletons in the frustum
	float					TemporalInScreenFade;
	/** This value represent the fade time we don't know what to do with TemporalInScreenFade because the
	 *	shadowMap is not visible or frustum-clipped.
	 *	Once the shadowMap will be visible again, This will be add/removed (according to TemporalInScreenFade
	 *	rules) to the TemporalInScreenFade.
	 *	NB: by default the value is 1 so the initial state is correct when you enable cast shadowMap!
	 */
	float					InScreenFadeAccum;

public:

	/// Constructor. NB: ptr is owned => shadowMap should no still live while smm dead.
	CShadowMap(CShadowMapManager *smm);
	~CShadowMap();

	/// create the Texture. It reset the texture if not of same size.
	void			initTexture(uint textSize);
	/// reset the Texture
	void			resetTexture();
	/// get the TextureSize
	uint32			getTextureSize() const {return _TextSize;}

	/// You can only get the texture for filling / use in a material.
	ITexture		*getTexture() const {return _Texture;}


	/// /name Tools
	// @{
	/** From A BBox in Object Space, the lightDir, and the (nearly) worldMatrix, build the Camera for common Render Projection
	 *	The caller has then just to do
	 *		driver->setFrustum(0,1,0,1,0,1,false);
	 *		driver->setupViewMatrix(cameraMatrix.inverted());
	 *		driver->setupModelMatrix(localPosMatrix);
	 *	Then render his mesh.
	 */
	void			buildCasterCameraMatrix(const NLMISC::CVector &lightDir, const NLMISC::CMatrix &localPosMatrix, const NLMISC::CAABBox &bbShape, NLMISC::CMatrix &cameraMatrix);

	/** From the Camera matrix computed with buildCasterCameraMatrix, compute the LocalProjectionMatrix, which modify the
	 *	J axis according to backPoint and Shadow Depth.
	 *	NB: automatically calls the buildClipInfoFromMatrix() method
	 */
	void			buildProjectionInfos(const NLMISC::CMatrix &cameraMatrix, const NLMISC::CVector &backPoint, float shadowMaxDepth);

	/** The ShadowMap Caster can call this method after setting LocalProjectionMatrix. It computes auto the
	 *	LocalClipPlanes and LocalBoundingBox from it. NB: don't use it if you use buildProjectionInfos().
	 */
	void			buildClipInfoFromMatrix();
	// @}

	/** Clamp Fades to 0-1. Additionaly reset the texture if DistanceFade>=1 or TemporalOutScreenFade>=1
	 *	See Implementation for Why. Additionally compile getFadeAround() and getFadeInScreen()
	 */
	void			processFades();

	/// return compiled max of DistanceFade and TemporalOutScreenFade.
	float			getFadeAround() const {return _FadeAround;}
	/// same but maximize with the TemporalInScreenFade;
	float			getFinalFade() const {return _FinalFade;}

// *************
private:
	NLMISC::CSmartPtr<ITexture>		_Texture;
	uint32							_TextSize;
	CShadowMapManager				*_ShadowMapManager;
	float							_FadeAround;
	float							_FinalFade;

};


// ***************************************************************************
/** Used to recompute the projection matrix, according to the receiver worldMatrix
 *  The problem is material don't support WorldSpace Coordinate Generation, but ObjectSpace ones.
 *	Hence must take back the coordinate in ObjectSpace before set textMat.
 */
class CShadowMapProjector
{
public:
	CShadowMapProjector();
	void	setWorldSpaceTextMat(const NLMISC::CMatrix &ws);
	void	applyToMaterial(const NLMISC::CMatrix &receiverWorldMatrix, CMaterial &material);

private:
	NLMISC::CMatrix			_WsTextMat;
	NLMISC::CMatrix			_XYZToUWVMatrix;
	NLMISC::CMatrix			_XYZToWUVMatrix;
};


} // NL3D


#endif // NL_SHADOW_MAP_H

/* End of shadow_map.h */
